<template>
	<view class="sleep_box">
		<view v-if="showX" class="showX">
			<view class="name_type" v-for="(name, index) in yNames" :key="index">
				<view class="dot" :style="{ backgroundColor: colors[index] }"></view>
				<view class="type">{{ name }}</view>
			</view>
		</view>
		<view class="sleep">
			<view v-if="showY" class="showY" @click="clearMark">
				<view class="y_name" v-for="(name, index) in yNames" :key="index">{{ name }}</view>
			</view>
			<view ref="sleepChildBox" class="sleep_child_box" :class="{ showY }" @touchmove="touchMove">
				<template v-if="render">
					<view v-for="(item, index) in n_list" :key="index" :data-index="index" class="sleep-child" :style="setTypeStyle(item, index)">
						<view v-if="typeList[index]" class="sleep-line" :style="setLineStyle(item, index)"></view>
						<view v-if="isDetail" v-show="index === maskIndex" class="sleep-mask" :style="setMaskStyle(item, index)"></view>
						<view class="sleep-mask" :style="setMaskStyle(item, index)" style="opacity: 0; z-index: 10" @click="selectMask(index)"></view>
					</view>
				</template>
			</view>
		</view>
		<view v-if="showTime" class="times" :class="{ showY }">
			<!-- <text class="time" v-for="(item, index) in times" :key="index"
				:style="{ left: item.left, right: item.right }">
				{{ item.time }}
			</text> -->
			<text class="time" v-for="(item, index) in times" :key="index">
				{{ item }}
			</text>
		</view>
	</view>
</template>
<script>
import dayjs from "dayjs";

export default {
	props: {
		value: {
			type: Array,
			default: () => [],
		},
		showY: {
			type: Boolean,
			default: false,
		},
		showX: {
			type: Boolean,
			default: false,
		},
		showTime: {
			type: Boolean,
			default: false,
		},
		width: {
			type: Number,
			default: 0,
		},
		height: {
			type: Number,
			default: 0,
		},
		isDetail: {
			type: Boolean,
			default: false,
		},
	},
	data() {
		return {
			colors: ["#0059B8", "#2486ED", "#7EBDFF", "#D1E7FF"].reverse(),
			yNames: ["深睡", "浅睡", "快速眼动", "醒来"].reverse(),
			// colors: ["#1B56EE", "#799EFF", "#FFD979", "#79FFDF"],
			// yNames: ["深睡", "浅睡", "快速眼动", "醒来"],
			render: false,
			sleepBox: {
				clientHeight: 1,
				clientWidth: 1,
				left: 0,
			},
			n_list: [],
			total: 1,
			totalWidth: 0,
			typeList: [],
			typeLeftX: [],

			renderFail: true,

			maskIndex: -1,

			cWidth: 0,

			times: [],
		};
	},
	watch: {
		value: {
			handler() {
				this.init();
			},
			deep: true,
		},
	},
	computed: {
		sleepVo() {
			if (this.maskIndex === -1) return;
			// console.log('this.maskIndex', this.maskIndex);
			const item = this.n_list[this.maskIndex];
			// console.log("item", item)
			return item;
		},
	},
	mounted() {
		setTimeout(this.init, 100);
	},
	methods: {
		init() {
			const newValue = this.value;
			let timeout = setTimeout(() => {
				this.renderFail = true;
			}, 6000);

			try {
				if (this.width && this.height) {
					this.renderChart(this.width, this.height, newValue);
				} else {
					const query = uni.createSelectorQuery().in(this);
					query
						.select(".sleep_child_box")
						.boundingClientRect((data) => {
							if (!data) {
								this.renderFail = true;
								return;
							}
							this.renderFail = false;
							clearTimeout(timeout);
							this.sleepBox.left = data.left;
							this.renderChart(data.width, data.height, newValue);
						})
						.exec();
				}
			} catch (error) {
				console.log("我报错了吗 ===>", error);
			}
		},
		reInit() {
			setTimeout(this.init, 1);
		},
		renderChart(width, height, newValue) {
			this.sleepBox.clientWidth = Math.floor(width);
			this.sleepBox.clientHeight = Math.floor(height);

			if (this.sleepBox.clientWidth === 0 || this.sleepBox.clientHeight === 0) {
				this.renderFail = true;
				return;
			}

			const splits = newValue.reduce((acc, curr, index, arr) => {
				if (index === 0) return acc;
				const prev = arr[index - 1];
				const { startTime } = curr;

				const diff = dayjs(startTime).diff(dayjs(prev.endTime), "minute");
				if (diff > 5) {
					acc.push({
						type: 4,
						minutes: diff,
						index: index,
						startTime: prev.endTime,
						end: startTime,
					});
				}
				return acc;
			}, []);

			splits.forEach((element, index) => {
				newValue.splice(element.index + index, 0, element);
			});

			const list = (newValue || [])
				.map((item) => ({
					type: item.type >= 4 ? 4 : item.type,
					minutes: Math.round(item.minutes),
					startTime: item.startTime,
					endTime: item.endTime,
				}))
				.reduce((acc, curr) => {
					if (acc.length === 0) {
						acc.push(curr);
					} else {
						const prev = acc[acc.length - 1];
						if (prev.type === curr.type) {
							prev.minutes += curr.minutes;
							prev.endTime = curr.endTime;
						} else {
							acc.push(curr);
						}
					}
					return acc;
				}, []);

			const defaultTotal = newValue.reduce((a, b) => Number(a) + Number(b["minutes"]), 1);
			const diff = this.sleepBox.clientWidth - defaultTotal;

			let final = [];
			if (diff > 5) {
				// this.n_list = list;
				final = list;
			} else {
				final = list.reduce((acc, curr) => {
					if (acc.length === 0) {
						acc.push(curr);
					} else {
						const prev = acc[acc.length - 1];
						if (prev.type === curr.type) {
							prev.minutes += curr.minutes;
						} else {
							acc.push(curr);
						}
					}
					return acc;
				}, []);
			}

			if (final.length !== 0) {
				this.total = final.reduce((a, b) => Number(a) + Number(b["minutes"]), 1);
			} else {
				this.total = 0;
			}
			this.typeList = [];
			this.sleepBox.clientWidth += final.length / 2;
			this.render = true;
			this.n_list = final;
			console.log("this.n_list", this.n_list);

			this.times = [dayjs(this.n_list[0].startTime).format("HH:mm"), dayjs(this.n_list[this.n_list.length - 1].endTime).format("HH:mm")];

			console.log("this.times", this.times);
		},
		setTypeStyle({ type, minutes = 1 }, index, next = false) {
			if (this.typeList[index]) {
				return this.typeList[index];
			}

			const box = this.sleepBox;
			const b_width = box.clientWidth;
			const singleHeight = box.clientHeight / 4;
			const contextHeight = this.showY ? singleHeight / 2 : singleHeight;
			const scale = b_width / this.total || 1;

			const width = Math.ceil(minutes * scale) + "px";

			let left = parseInt(width) * index + "px";
			let prev = null;
			if (index !== 0) {
				prev = this.typeList[index - 1];
				left = (prev?.__offset || 0) - index + "px";
			}

			const c_type = (5 - type) % 4 || 4;
			const backgroundColor = this.colors[c_type - 1];
			const top = singleHeight * (c_type - 1) + contextHeight / 2 + "px";

			this.totalWidth += parseInt(width);

			const style = {
				width,
				height: contextHeight + "px",
				top,
				left,
				backgroundColor,
				__offset: (prev?.__offset || 0) + parseInt(width),
				// paddingTop: contextHeight / 2 + "px",
			};
			this.typeList[index] = style;
			this.typeLeftX[index] = parseInt(style.left);

			return style;
		},
		setLineStyle({ type }, index) {
			if (index === 0) {
				return {};
			}
			const prev = this.n_list[index - 1];
			if (prev.type === type) {
				return {};
			}

			const c_type = (5 - type) % 4 || 4;
			const p_type = (5 - prev.type) % 4 || 4;

			const box = this.sleepBox;
			const singleHeight = box.clientHeight / 4;
			const contextHeight = singleHeight / 2;
			// const backgroundColor = this.colors[type - 1];
			// const prevBackgroundColor = this.colors[prev.type - 1];
			const backgroundColor = this.colors[c_type - 1];
			const prevBackgroundColor = this.colors[p_type - 1];

			const background =
				// type > prev.type
				c_type > p_type
					? `linear-gradient(to top, ${backgroundColor} 0%, ${prevBackgroundColor} 100%)`
					: `linear-gradient(to top, ${prevBackgroundColor} 0%, ${backgroundColor} 100%)`;

			const child = this.typeList[index];
			const prevChild = this.typeList[index - 1];
			const c_top = parseInt(child.top);
			const p_top = parseInt(prevChild.top);

			const height = Math.abs(c_top - p_top);
			let top = 0;

			if (type < prev.type) {
				top = contextHeight / 2;
			} else {
				top = -height + contextHeight / 2;
			}

			if (c_type < p_type) {
				top = contextHeight / 2;
			} else {
				top = -height + contextHeight / 2;
			}

			top = top + "px";

			return {
				width: "1px",
				background,
				height: height + "px",
				left: "0px",
				top: top,
			};
		},

		setMaskStyle(_, index) {
			const item = this.typeList[index];
			return {
				width: "100%",
				height: this.sleepBox.clientHeight + "px",
				left: 0,
				backgroundColor: "rgba(255, 255, 255, 0.1)",
				top: -parseInt(item.top) + "px",
			};
		},

		touchMove(event) {
			if (!this.isDetail) return;
			const clientX = event.touches[0].clientX - this.sleepBox.left;
			if (clientX < -10 || clientX > this.sleepBox.clientWidth) {
				this.maskIndex = -1;
				this.$emit("change", null);
				return;
			}
			this.maskIndex = this.findNearestIndex(this.typeLeftX, clientX);

			this.$emit("change", this.sleepVo);
		},

		clearMark() {
			this.maskIndex = -1;
			this.$emit("change", null);
		},

		selectMask(index) {
			this.maskIndex = index;
			this.$emit("change", this.sleepVo);
		},

		findNearestIndex(arr, x) {
			const n = arr.length;

			// 边界条件
			if (n === 0) return -1; // 数组为空
			if (x <= arr[0]) return 0; // x 小于等于最小值
			if (x >= arr[n - 1]) return n - 1; // x 大于等于最大值

			let left = 0;
			let right = n - 1;

			// 二分查找
			while (left < right) {
				const mid = Math.floor((left + right) / 2);
				if (arr[mid] < x) {
					left = mid + 1;
				} else {
					right = mid;
				}
			}

			// 此时 left 是第一个大于等于 x 的元素
			// 比较 left 和 left - 1 的值，返回最近的下标
			if (left > 0 && Math.abs(arr[left] - x) >= Math.abs(arr[left - 1] - x)) {
				return left - 1;
			}
			return left;
		},
	},
};
</script>
<style lang="scss" scoped>
.sleep_box {
	width: 100%;
	height: 50%;
	display: flex;
	justify-content: center;
	align-items: center;
	flex-direction: column;
	position: relative;

	.showX {
		display: flex;
		justify-content: space-around;
		align-items: center;
		width: 80%;
		margin: 0 auto;
		margin-bottom: 39rpx;

		.name_type {
			display: flex;
			justify-content: center;
			align-items: center;

			.dot {
				width: 12rpx;
				height: 12rpx;
				border-radius: 50%;
				margin-right: 8rpx;
			}

			.type {
				font-family: PingFangSC, PingFang SC;
				font-weight: 400;
				font-size: 24rpx;
				color: #5b616c;
				line-height: 33rpx;
				text-align: left;
				font-style: normal;
			}
		}
	}
}

.Y_background {
	width: 100%;
	height: 100%;
	position: relative;
	z-index: 1;
	display: flex;
	color: white;
	font-size: 24rpx;

	.Y_box {
		width: auto;
		height: 100%;
		display: flex;
		flex-direction: column;
		flex-wrap: nowrap;

		.Y {
			flex: 1;
			width: 100%;
			display: flex;
			justify-content: flex-start;
			align-items: center;
		}
	}
}

.sleep {
	position: relative;
	width: 100%;
	height: 100%;
	display: flex;
	justify-content: flex-start;
	align-items: center;

	.showY {
		position: absolute;
		width: 100%;
		height: 100%;
		left: 0;
		top: 0;

		display: flex;
		justify-content: flex-start;
		align-items: center;
		flex-direction: column;

		.y_name {
			width: 100%;
			border-bottom: 1rpx dashed rgba(255, 255, 255, 0.16);
			color: rgba(175, 175, 175, 1);
			flex: 1;
			display: flex;
			justify-content: flex-start;
			align-items: center;
			font-size: 20rpx;
		}
	}

	.sleep_child_box {
		width: 100%;
		height: 100%;
		position: relative;

		&.showY {
			width: calc(100% - 120rpx);
			margin-left: auto;
		}
	}

	.sleep-child {
		position: absolute;
		border-radius: 10rpx;
		z-index: 2;
	}

	.sleep-line {
		position: absolute;
		border-radius: 10rpx;
		transform-origin: top left;
		z-index: 1;
		opacity: 0.8;
	}

	.sleep-mask {
		width: 100%;
		background-color: rgba(0, 0, 0, 0.01);
		position: absolute;
		left: 0;
		top: 0;
	}
}

.time-line {
	display: flex;
	justify-content: flex-start;
	align-items: center;
	width: 100%;
	margin-top: 10rpx;

	view {
		font-family: PingFangSC, PingFang SC;
		font-weight: 400;
		font-size: 20rpx;
		color: #c4c7cc;
		line-height: 28rpx;
		text-align: left;
		font-style: normal;
	}
}

.sleep_Y_background {
	position: absolute;
	width: 100%;
	height: 100%;
	display: flex;
	justify-content: flex-start;
	align-items: center;
	flex-direction: column;

	.Y {
		flex: 1;
		width: 100%;
		display: flex;
		justify-content: flex-start;
		align-items: center;

		& + .Y {
			border-top: 1px dashed #ffffff29;
		}
	}
}

.times {
	padding-left: 120rpx;
	padding-top: 20rpx;
	width: 100%;
	height: auto;
	font-size: 20rpx;
	display: flex;
	justify-content: space-between;
	align-items: center;
	align-content: center;
}
</style>
